home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / ops.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  39KB  |  1,736 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * ops.c: implementation of various operators: doshift, dodelete, dotilde,
  11.  *          dochange, doyank, doput, dojoin
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "globals.h"
  16. #include "proto.h"
  17. #include "param.h"
  18. #include "ops.h"
  19.  
  20. /*
  21.  * We have one yank buffer for normal yanks and puts, nine yank buffers for
  22.  * deletes and 26 yank buffers for use by name.
  23.  * Each yank buffer is an array of pointers to lines.
  24.  */
  25. static struct yankbuf
  26. {
  27.     char_u        **y_array;        /* pointer to array of line pointers */
  28.     linenr_t     y_size;         /* number of lines in y_array */
  29.     char_u        y_type;         /* MLINE, MCHAR or MBLOCK */
  30. } y_buf[36];                    /* 0..9 = number buffers, 10..35 = char buffers */
  31.  
  32. static struct    yankbuf *y_current;        /* ptr to current yank buffer */
  33. static int        yankappend;                /* TRUE when appending */
  34. static struct    yankbuf *y_previous = NULL; /* ptr to last written yank buffer */
  35.  
  36. static void        get_yank_buffer __ARGS((int));
  37. static int        stuff_yank __ARGS((int, char_u *));
  38. static void        free_yank __ARGS((long));
  39. static void        free_yank_all __ARGS((void));
  40. static void        block_prep __ARGS((linenr_t, int));
  41.  
  42. /* variables use by block_prep, dodelete and doyank */
  43. static int        startspaces;
  44. static int        endspaces;
  45. static int        textlen;
  46. static char_u        *textstart;
  47. static colnr_t    textcol;
  48.  
  49. /*
  50.  * doshift - handle a shift operation
  51.  */
  52.     void
  53. doshift(op, curs_top, amount)
  54.     int             op;
  55.     int                curs_top;
  56.     int                amount;
  57. {
  58.     register long    i;
  59.     int                first_char;
  60.  
  61.     if (!u_save((linenr_t)(curwin->w_cursor.lnum - 1), (linenr_t)(curwin->w_cursor.lnum + nlines)))
  62.         return;
  63.  
  64.     for (i = nlines; --i >= 0; )
  65.     {
  66.         first_char = *ml_get(curwin->w_cursor.lnum);
  67.         if (first_char == NUL)                            /* empty line */
  68.             curwin->w_cursor.col = 0;
  69.         /*
  70.          * Don't move the line right if it starts with # and p_si is set.
  71.          */
  72.         else if (!curbuf->b_p_si || first_char != '#')
  73.         {
  74.             /* if (Visual_block)
  75.                     shift the block, not the whole line
  76.             else */
  77.                 shift_line(op == LSHIFT, p_sr, amount);
  78.         }
  79.         ++curwin->w_cursor.lnum;
  80.     }
  81.  
  82.     if (curs_top)            /* put cursor on first line, for ">>" */
  83.         curwin->w_cursor.lnum -= nlines;
  84.     else
  85.         --curwin->w_cursor.lnum;        /* put cursor on last line, for ":>" */
  86.     updateScreen(CURSUPD);
  87.  
  88.     if (nlines > p_report)
  89.         smsg((char_u *)"%ld line%s %ced", nlines, plural(nlines),
  90.                                     (op == RSHIFT) ? '>' : '<');
  91. }
  92.  
  93. /*
  94.  * shift the current line one shiftwidth left (if left != 0) or right
  95.  * leaves cursor on first blank in the line
  96.  */
  97.     void
  98. shift_line(left, round, amount)
  99.     int left;
  100.     int    round;
  101.     int    amount;
  102. {
  103.     register int count;
  104.     register int i, j;
  105.     int p_sw = (int)curbuf->b_p_sw;
  106.  
  107.     count = get_indent();            /* get current indent */
  108.  
  109.     if (round)                        /* round off indent */
  110.     {
  111.         i = count / p_sw;            /* number of p_sw rounded down */
  112.         j = count % p_sw;            /* extra spaces */
  113.         if (j && left)                /* first remove extra spaces */
  114.             --amount;
  115.         if (left)
  116.         {
  117.             i -= amount;
  118.             if (i < 0)
  119.                 i = 0;
  120.         }
  121.         else
  122.             i += amount;
  123.         count = i * p_sw;
  124.     }
  125.     else                /* original vi indent */
  126.     {
  127.         if (left)
  128.         {
  129.             count -= p_sw * amount;
  130.             if (count < 0)
  131.                 count = 0;
  132.         }
  133.         else
  134.             count += p_sw * amount;
  135.     }
  136.     set_indent(count, TRUE);        /* set new indent */
  137. }
  138.  
  139. /*
  140.  * check if character is name of yank buffer
  141.  * Note: There is no check for 0 (default register), caller should do this
  142.  */
  143.      int
  144. is_yank_buffer(c, write)
  145.     int        c;
  146.     int        write;        /* if TRUE check for writable buffers */
  147. {
  148.     if (isalnum(c) || (!write && strchr(".%:", c) != NULL) || c == '"')
  149.         return TRUE;
  150.     return FALSE;
  151. }
  152.  
  153. /*
  154.  * Set y_current and yankappend, according to the value of yankbuffer.
  155.  *
  156.  * If yankbuffer is 0 and writing, use buffer 0
  157.  * If yankbuffer is 0 and reading, use previous buffer
  158.  */
  159.     static void
  160. get_yank_buffer(writing)
  161.     int        writing;
  162. {
  163.     register int i;
  164.  
  165.     yankappend = FALSE;
  166.     if (((yankbuffer == 0 && !writing) || yankbuffer == '"') && y_previous != NULL)
  167.     {
  168.         y_current = y_previous;
  169.         return;
  170.     }
  171.     i = yankbuffer;
  172.     if (isdigit(i))
  173.         i -= '0';
  174.     else if (islower(i))
  175.         i -= 'a' - 10;
  176.     else if (isupper(i))
  177.     {
  178.         i -= 'A' - 10;
  179.         yankappend = TRUE;
  180.     }
  181.     else            /* not 0-9, a-z or A-Z: use buffer 0 */
  182.         i = 0;
  183.     y_current = &(y_buf[i]);
  184.     if (writing)        /* remember the buffer we write into for doput() */
  185.         y_previous = y_current;
  186. }
  187.  
  188. /*
  189.  * start or stop recording into a yank buffer
  190.  *
  191.  * return FAIL for failure, OK otherwise
  192.  */
  193.     int
  194. dorecord(c)
  195.     int c;
  196. {
  197.     char_u        *p;
  198.     static int    bufname;
  199.     int            retval;
  200.  
  201.     if (Recording == FALSE)         /* start recording */
  202.     {
  203.         if (!isalnum(c) && c != '"')    /* registers 0-9, a-z and " are allowed */
  204.             retval = FAIL;
  205.         else
  206.         {
  207.             Recording = TRUE;
  208.             showmode();
  209.             bufname = c;
  210.             retval = OK;
  211.         }
  212.     }
  213.     else                            /* stop recording */
  214.     {
  215.         Recording = FALSE;
  216.         MSG("");
  217.             /* the trailing 'q' command will not have been put in the buffer */
  218.         p = get_recorded();
  219.         if (p == NULL)
  220.             retval = FAIL;
  221.         else
  222.             retval = (stuff_yank(bufname, p));
  223.     }
  224.     return retval;
  225. }
  226.  
  227. /*
  228.  * stuff string 'p' into yank buffer 'bufname' (append if uppercase)
  229.  * 'p' is assumed to be alloced.
  230.  *
  231.  * return FAIL for failure, OK otherwise
  232.  */
  233.     static int
  234. stuff_yank(bufname, p)
  235.     int bufname;
  236.     char_u *p;
  237. {
  238.     char_u *lp;
  239.     char_u **pp;
  240.  
  241.     yankbuffer = bufname;
  242.                                             /* check for read-only buffer */
  243.     if (yankbuffer != 0 && !is_yank_buffer(yankbuffer, TRUE))
  244.         return FAIL;
  245.     get_yank_buffer(TRUE);
  246.     if (yankappend && y_current->y_array != NULL)
  247.     {
  248.         pp = &(y_current->y_array[y_current->y_size - 1]);
  249.         lp = lalloc((long_u)(STRLEN(*pp) + STRLEN(p) + 1), TRUE);
  250.         if (lp == NULL)
  251.         {
  252.             free(p);
  253.             return FAIL;
  254.         }
  255.         STRCPY(lp, *pp);
  256.         STRCAT(lp, p);
  257.         free(p);
  258.         free(*pp);
  259.         *pp = lp;
  260.     }
  261.     else
  262.     {
  263.         free_yank_all();
  264.         if ((y_current->y_array = (char_u **)alloc((unsigned)sizeof(char_u *))) == NULL)
  265.         {
  266.             free(p);
  267.             return FAIL;
  268.         }
  269.         y_current->y_array[0] = p;
  270.         y_current->y_size = 1;
  271.         y_current->y_type = MCHAR;    /* used to be MLINE, why? */
  272.     }
  273.     return OK;
  274. }
  275.  
  276. /*
  277.  * execute a yank buffer (register): copy it into the stuff buffer
  278.  *
  279.  * return FAIL for failure, OK otherwise
  280.  */
  281.     int
  282. doexecbuf(c)
  283.     int c;
  284. {
  285.     static int lastc = NUL;
  286.     long i;
  287.  
  288.     if (c == '@')                    /* repeat previous one */
  289.         c = lastc;
  290.     if (!is_yank_buffer(c, FALSE))    /* check for valid buffer */
  291.         return FAIL;
  292.     lastc = c;
  293.  
  294.     if (c == ':')                    /* use last command line */
  295.     {
  296.         if (last_cmdline == NULL)
  297.         {
  298.             EMSG(e_nolastcmd);
  299.             return FAIL;
  300.         }
  301.         free(new_last_cmdline);        /* don't keep the command line containing @: */
  302.         new_last_cmdline = NULL;
  303.         if (ins_typestr((char_u *)"\n", FALSE) == FAIL)
  304.             return FAIL;
  305.         if (ins_typestr(last_cmdline, FALSE) == FAIL)
  306.             return FAIL;
  307.     }
  308.     else
  309.     {
  310.         yankbuffer = c;
  311.         get_yank_buffer(FALSE);
  312.         if (y_current->y_array == NULL)
  313.             return FAIL;
  314.  
  315.         for (i = y_current->y_size; --i >= 0; )
  316.         {
  317.         /* insert newline between lines and after last line if type is MLINE */
  318.             if (y_current->y_type == MLINE || i < y_current->y_size - 1)
  319.             {
  320.                 if (ins_typestr((char_u *)"\n", FALSE) == FAIL)
  321.                     return FAIL;
  322.             }
  323.             if (ins_typestr(y_current->y_array[i], FALSE) == FAIL)
  324.                 return FAIL;
  325.         }
  326.         Exec_reg = TRUE;        /* disable the 'q' command */
  327.     }
  328.     return OK;
  329. }
  330.  
  331. /*
  332.  * insert a yank buffer: copy it into the Read buffer
  333.  * used by CTRL-R command in insert mode
  334.  *
  335.  * return FAIL for failure, OK otherwise
  336.  */
  337.     int
  338. insertbuf(c)
  339.     int c;
  340. {
  341.     long i;
  342.  
  343.     /*
  344.      * It is possible to get into an endless loop by having CTRL-R a in
  345.      * register a and then, in insert mode, doing CTRL-R a.
  346.      * If you hit CTRL-C, the loop will be broken here.
  347.      */
  348.     breakcheck();
  349.     if (got_int)
  350.         return FAIL;
  351.  
  352.     if (!is_yank_buffer(c, FALSE))        /* check for valid buffer */
  353.         return FAIL;
  354.  
  355.     if (c == '.')                        /* insert last inserted text */
  356.     {
  357.         stuff_inserted(NUL, 1L, TRUE);
  358.         return OK;
  359.     }
  360.  
  361.     if (c == '%')                        /* insert file name */
  362.     {
  363.         if (check_fname() == FAIL)
  364.             return FAIL;
  365.         stuffReadbuff(curbuf->b_xfilename);
  366.         return OK;
  367.     }
  368.  
  369.     if (c == ':')                        /* insert last command line */
  370.     {
  371.         if (last_cmdline == NULL)
  372.         {
  373.             EMSG(e_nolastcmd);
  374.             return FAIL;
  375.         }
  376.         stuffReadbuff(last_cmdline);
  377.         return OK;
  378.     }
  379.  
  380.     yankbuffer = c;
  381.     get_yank_buffer(FALSE);
  382.     if (y_current->y_array == NULL)
  383.         return FAIL;
  384.  
  385.     for (i = 0; i < y_current->y_size; ++i)
  386.     {
  387.         stuffReadbuff(y_current->y_array[i]);
  388.     /* insert newline between lines and after last line if type is MLINE */
  389.         if (y_current->y_type == MLINE || i < y_current->y_size - 1)
  390.             stuffReadbuff((char_u *)"\n");
  391.     }
  392.     return OK;
  393. }
  394.  
  395. /*
  396.  * dodelete - handle a delete operation
  397.  */
  398.     void
  399. dodelete()
  400. {
  401.     register int    n;
  402.     linenr_t        lnum;
  403.     char_u            *ptr;
  404.     char_u            *new, *old;
  405.     linenr_t        old_lcount = curbuf->b_ml.ml_line_count;
  406.     int                did_yank = FALSE;
  407.  
  408.     /*
  409.      * Imitate the strange Vi behaviour: If the delete spans more than one line
  410.      * and mtype == MCHAR and the result is a blank line, make the delete
  411.      * linewise. Don't do this for the change command.
  412.      */
  413.     if (mtype == MCHAR && nlines > 1 && operator == DELETE)
  414.     {
  415.         ptr = ml_get(curbuf->b_endop.lnum) + curbuf->b_endop.col + mincl;
  416.         skipspace(&ptr);
  417.         if (*ptr == NUL && inindent())
  418.             mtype = MLINE;
  419.     }
  420.  
  421. /*
  422.  * If a yank buffer was specified, put the deleted text into that buffer
  423.  */
  424.     if (yankbuffer != 0)
  425.     {
  426.                                         /* check for read-only buffer */
  427.         if (!is_yank_buffer(yankbuffer, TRUE))
  428.         {
  429.             beep();
  430.             return;
  431.         }
  432.         get_yank_buffer(TRUE);            /* yank into specified buffer */
  433.         if (doyank(TRUE) == OK)
  434.             did_yank = TRUE;
  435.     }
  436.  
  437. /*
  438.  * Put deleted text into register 1 and shift number buffers if
  439.  * the delete contains a line break.
  440.  * Overruled when a yankbuffer has been specified!
  441.  */
  442.     if (yankbuffer != 0 || mtype == MLINE || nlines > 1)
  443.     {
  444.         y_current = &y_buf[9];
  445.         free_yank_all();                /* free buffer nine */
  446.         for (n = 9; n > 1; --n)
  447.             y_buf[n] = y_buf[n - 1];
  448.         y_previous = y_current = &y_buf[1];
  449.         y_buf[1].y_array = NULL;        /* set buffer one to empty */
  450.         yankbuffer = 0;
  451.     }
  452.     else if (yankbuffer == 0)            /* yank into unnamed buffer */
  453.         get_yank_buffer(TRUE);
  454.  
  455.     /*
  456.      * Do a yank of whatever we're about to delete. If there's too much stuff
  457.      * to fit in the yank buffer, then get a confirmation before doing the
  458.      * delete. This is crude, but simple. And it avoids doing a delete of
  459.      * something we can't put back if we want.
  460.      */
  461.     if (yankbuffer == 0 && doyank(TRUE) == OK)
  462.         did_yank = TRUE;
  463.  
  464.     if (!did_yank)
  465.     {
  466.         if (ask_yesno((char_u *)"cannot yank; delete anyway") != 'y')
  467.         {
  468.             emsg(e_abort);
  469.             return;
  470.         }
  471.     }
  472.  
  473. /*
  474.  * block mode
  475.  */
  476.     if (Visual_block)
  477.     {
  478.         if (!u_save((linenr_t)(curbuf->b_startop.lnum - 1), (linenr_t)(curbuf->b_endop.lnum + 1)))
  479.             return;
  480.  
  481.         for (lnum = curwin->w_cursor.lnum; curwin->w_cursor.lnum <= curbuf->b_endop.lnum; ++curwin->w_cursor.lnum)
  482.         {
  483.             block_prep(curwin->w_cursor.lnum, TRUE);
  484.             if (textlen == 0)        /* nothing to delete */
  485.                 continue;
  486.  
  487.         /*
  488.          * If we delete a TAB, it may be replaced by several characters.
  489.          * Thus the number of characters may increase!
  490.          */
  491.             n = textlen - startspaces - endspaces;        /* number of chars deleted */
  492.             old = ml_get(curwin->w_cursor.lnum);
  493.             new = alloc((unsigned)STRLEN(old) + 1 - n);
  494.             if (new == NULL)
  495.                 continue;
  496.         /* copy up to deleted part */
  497.             memmove((char *)new, (char *)old, (size_t)textcol);
  498.         /* insert spaces */
  499.             copy_spaces(new + textcol, (size_t)(startspaces + endspaces));
  500.         /* copy the part after the deleted part */
  501.             old += textcol + textlen;
  502.             memmove((char *)new + textcol + startspaces + endspaces,
  503.                                     (char *)old, STRLEN(old) + 1);
  504.         /* replace the line */
  505.             ml_replace(curwin->w_cursor.lnum, new, FALSE);
  506.         }
  507.         curwin->w_cursor.lnum = lnum;
  508.         CHANGED;
  509.         updateScreen(VALID_TO_CURSCHAR);
  510.         nlines = 0;        /* no lines deleted */
  511.     }
  512.     else if (mtype == MLINE)
  513.     {
  514.         if (operator == CHANGE)
  515.         {
  516.             dellines((long)(nlines - 1), TRUE, TRUE);
  517.             if (!u_save_cursor())
  518.                 return;
  519.             if (curbuf->b_p_ai)                /* don't delete indent */
  520.             {
  521.                 beginline(TRUE);            /* put cursor on first non-white */
  522.                 did_ai = TRUE;                /* delete the indent when ESC hit */
  523.             }
  524.             while (delchar(FALSE) == OK)    /* slow but simple */
  525.                 ;
  526.             if (curwin->w_cursor.col > 0)
  527.                 --curwin->w_cursor.col;        /* put cursor on last char in line */
  528.         }
  529.         else
  530.         {
  531.             dellines(nlines, TRUE, TRUE);
  532.         }
  533.         u_clearline();    /* "U" command should not be possible after "dd" */
  534.         beginline(TRUE);
  535.     }
  536.     else if (nlines == 1)        /* delete characters within one line */
  537.     {
  538.         if (!u_save_cursor())
  539.             return;
  540.         n = curbuf->b_endop.col - curbuf->b_startop.col + 1 - !mincl;
  541.         while (n-- > 0)
  542.             if (delchar(TRUE) == FAIL)
  543.                 break;
  544.     }
  545.     else                        /* delete characters between lines */
  546.     {
  547.         if (!u_save_cursor())    /* save first line for undo */
  548.             return;
  549.         n = curwin->w_cursor.col;
  550.         while (curwin->w_cursor.col >= n)    /* delete from cursor to end of line */
  551.             if (delchar(TRUE) == FAIL)
  552.                 break;
  553.  
  554.         curbuf->b_startop = curwin->w_cursor;        /* remember curwin->w_cursor */
  555.         ++curwin->w_cursor.lnum;
  556.         dellines((long)(nlines - 2), TRUE, TRUE);    /* includes save for undo */
  557.  
  558.         if (!u_save_cursor())    /* save last line for undo */
  559.             return;
  560.         n = curbuf->b_endop.col - !mincl;
  561.         curwin->w_cursor.col = 0;
  562.         while (n-- >= 0)        /* delete from start of line until endop */
  563.             if (delchar(TRUE) == FAIL)
  564.                 break;
  565.         curwin->w_cursor = curbuf->b_startop;        /* restore curwin->w_cursor */
  566.         (void)dojoin(FALSE, TRUE);
  567.     }
  568.  
  569.     if ((mtype == MCHAR && nlines == 1) || operator == CHANGE)
  570.     {
  571.         cursupdate();
  572.         updateline();
  573.     }
  574.     else
  575.         updateScreen(CURSUPD);
  576.  
  577.     msgmore(curbuf->b_ml.ml_line_count - old_lcount);
  578.  
  579.         /* correct endop for deleted text (for "']" command) */
  580.     if (Visual_block)
  581.         curbuf->b_endop.col = curbuf->b_startop.col;
  582.     else
  583.         curbuf->b_endop = curbuf->b_startop;
  584. }
  585.  
  586. /*
  587.  * dotilde - handle the (non-standard vi) tilde operator
  588.  */
  589.     void
  590. dotilde()
  591. {
  592.     FPOS pos;
  593.  
  594.     if (!u_save((linenr_t)(curbuf->b_startop.lnum - 1), (linenr_t)(curbuf->b_endop.lnum + 1)))
  595.         return;
  596.  
  597.     pos = curbuf->b_startop;
  598.     if (Visual_block)        /* block mode */
  599.     {
  600.         for (; pos.lnum <= curbuf->b_endop.lnum; ++pos.lnum)
  601.         {
  602.             block_prep(pos.lnum, FALSE);
  603.             pos.col = textcol;
  604.             while (--textlen >= 0)
  605.             {
  606.                 swapchar(&pos);
  607.                 if (inc(&pos) == -1)    /* at end of file */
  608.                     break;
  609.             }
  610.         }
  611.     }
  612.     else            /* not block mode */
  613.     {
  614.         if (mtype == MLINE)
  615.         {
  616.                 pos.col = 0;
  617.                 curbuf->b_endop.col = STRLEN(ml_get(curbuf->b_endop.lnum));
  618.                 if (curbuf->b_endop.col)
  619.                         --curbuf->b_endop.col;
  620.         }
  621.         else if (!mincl)
  622.             dec(&(curbuf->b_endop));
  623.  
  624.         while (ltoreq(pos, curbuf->b_endop))
  625.         {
  626.             swapchar(&pos);
  627.             if (inc(&pos) == -1)    /* at end of file */
  628.                 break;
  629.         }
  630.     }
  631.  
  632.     if (mtype == MCHAR && nlines == 1 && !Visual_block)
  633.     {
  634.         cursupdate();
  635.         updateline();
  636.     }
  637.     else
  638.         updateScreen(CURSUPD);
  639.  
  640.     if (nlines > p_report)
  641.             smsg((char_u *)"%ld line%s ~ed", nlines, plural(nlines));
  642. }
  643.  
  644. /*
  645.  * If operator == UPPER: make uppercase,
  646.  * if operator == LOWER: make lowercase,
  647.  * else swap case of character at 'pos'
  648.  */
  649.     void
  650. swapchar(pos)
  651.     FPOS    *pos;
  652. {
  653.     int        c;
  654.  
  655.     c = gchar(pos);
  656.     if (islower(c) && operator != LOWER)
  657.     {
  658.         pchar(*pos, toupper(c));
  659.         CHANGED;
  660.     }
  661.     else if (isupper(c) && operator != UPPER)
  662.     {
  663.         pchar(*pos, tolower(c));
  664.         CHANGED;
  665.     }
  666. }
  667.  
  668. /*
  669.  * dochange - handle a change operation
  670.  */
  671.     void
  672. dochange()
  673. {
  674.     register colnr_t            l;
  675.  
  676.     l = curbuf->b_startop.col;
  677.  
  678.     if (!no_op)
  679.         dodelete();
  680.  
  681.     if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum))
  682.         inc_cursor();
  683.  
  684.     startinsert(NUL, FALSE, (linenr_t)1);
  685. }
  686.  
  687. /*
  688.  * set all the yank buffers to empty (called from main())
  689.  */
  690.     void
  691. init_yank()
  692. {
  693.         register int i;
  694.  
  695.         for (i = 0; i < 36; ++i)
  696.                 y_buf[i].y_array = NULL;
  697. }
  698.  
  699. /*
  700.  * Free "n" lines from the current yank buffer.
  701.  * Called for normal freeing and in case of error.
  702.  */
  703.     static void
  704. free_yank(n)
  705.     long n;
  706. {
  707.     if (y_current->y_array != NULL)
  708.     {
  709.         register long i;
  710.  
  711.         for (i = n; --i >= 0; )
  712.         {
  713.             if (i % 1000 == 999)                    /* this may take a while */
  714.                 smsg((char_u *)"freeing %ld lines", i + 1);
  715.             free(y_current->y_array[i]);
  716.         }
  717.         free(y_current->y_array);
  718.         y_current->y_array = NULL;
  719.         if (n >= 1000)
  720.             MSG("");
  721.     }
  722. }
  723.  
  724.     static void
  725. free_yank_all()
  726. {
  727.         free_yank(y_current->y_size);
  728. }
  729.  
  730. /*
  731.  * Yank the text between curwin->w_cursor and startpos into a yank buffer.
  732.  * If we are to append ("uppercase), we first yank into a new yank buffer and
  733.  * then concatenate the old and the new one (so we keep the old one in case
  734.  * of out-of-memory).
  735.  *
  736.  * return FAIL for failure, OK otherwise
  737.  */
  738.     int
  739. doyank(deleting)
  740.     int deleting;
  741. {
  742.     long                 i;                /* index in y_array[] */
  743.     struct yankbuf        *curr;            /* copy of y_current */
  744.     struct yankbuf        new;             /* new yank buffer when appending */
  745.     char_u                **new_ptr;
  746.     register linenr_t    lnum;            /* current line number */
  747.     long                 j;
  748.     int                    yanktype = mtype;
  749.     long                yanklines = nlines;
  750.     linenr_t            yankendlnum = curbuf->b_endop.lnum;
  751.  
  752.     char_u                *pnew;
  753.  
  754.                                     /* check for read-only buffer */
  755.     if (yankbuffer != 0 && !is_yank_buffer(yankbuffer, TRUE))
  756.     {
  757.         beep();
  758.         return FAIL;
  759.     }
  760.     if (!deleting)                    /* dodelete() already set y_current */
  761.         get_yank_buffer(TRUE);
  762.  
  763.     curr = y_current;
  764.     if (yankappend && y_current->y_array != NULL) /* append to existing contents */
  765.         y_current = &new;
  766.     else
  767.         free_yank_all();        /* free previously yanked lines */
  768.  
  769. /*
  770.  * If the cursor was in column 1 before and after the movement, the
  771.  * yank is always linewise.
  772.  */
  773.     if (mtype == MCHAR && curbuf->b_startop.col == 0 && curbuf->b_endop.col == 0 && nlines > 1)
  774.     {
  775.         yanktype = MLINE;
  776.         if (mincl == FALSE && yankendlnum > curbuf->b_startop.lnum)
  777.         {
  778.             --yankendlnum;
  779.             --yanklines;
  780.         }
  781.     }
  782.  
  783.     y_current->y_size = yanklines;
  784.     y_current->y_type = yanktype;    /* set the yank buffer type */
  785.     y_current->y_array = (char_u **)lalloc((long_u)(sizeof(char_u *) * yanklines), TRUE);
  786.  
  787.     if (y_current->y_array == NULL)
  788.     {
  789.         y_current = curr;
  790.         return FAIL;
  791.     }
  792.  
  793.     i = 0;
  794.     lnum = curbuf->b_startop.lnum;
  795.  
  796.     if (Visual_block)
  797.     {
  798. /*
  799.  * block mode
  800.  */
  801.         y_current->y_type = MBLOCK;    /* set the yank buffer type */
  802.         for ( ; lnum <= yankendlnum; ++lnum)
  803.         {
  804.             block_prep(lnum, FALSE);
  805.             if ((pnew = alloc(startspaces + endspaces + textlen + 1)) == NULL)
  806.                 goto fail;
  807.             y_current->y_array[i++] = pnew;
  808.             copy_spaces(pnew, (size_t)startspaces);
  809.             pnew += startspaces;
  810.             STRNCPY(pnew, textstart, (size_t)textlen);
  811.             pnew += textlen;
  812.             copy_spaces(pnew, (size_t)endspaces);
  813.             pnew += endspaces;
  814.             *pnew = NUL;
  815.         }
  816.     }
  817.     else
  818.     {
  819. /*
  820.  * there are three parts for non-block mode:
  821.  * 1. if yanktype != MLINE yank last part of the top line
  822.  * 2. yank the lines between startop and endop, inclusive when yanktype == MLINE
  823.  * 3. if yanktype != MLINE yank first part of the bot line
  824.  */
  825.         if (yanktype != MLINE)
  826.         {
  827.             if (yanklines == 1)        /* startop and endop on same line */
  828.             {
  829.                     j = curbuf->b_endop.col - curbuf->b_startop.col + 1 - !mincl;
  830.                     if ((y_current->y_array[0] = strnsave(ml_get(lnum) + curbuf->b_startop.col, (int)j)) == NULL)
  831.                     {
  832.     fail:
  833.                             free_yank(i);    /* free the lines that we allocated */
  834.                             y_current = curr;
  835.                             return FAIL;
  836.                     }
  837.                     goto success;
  838.             }
  839.             if ((y_current->y_array[0] = strsave(ml_get(lnum++) + curbuf->b_startop.col)) == NULL)
  840.                     goto fail;
  841.             ++i;
  842.         }
  843.  
  844.         while (yanktype == MLINE ? (lnum <= yankendlnum) : (lnum < yankendlnum))
  845.         {
  846.             if ((y_current->y_array[i] = strsave(ml_get(lnum++))) == NULL)
  847.                     goto fail;
  848.             ++i;
  849.         }
  850.         if (yanktype != MLINE)
  851.         {
  852.             if ((y_current->y_array[i] = strnsave(ml_get(yankendlnum), curbuf->b_endop.col + 1 - !mincl)) == NULL)
  853.                     goto fail;
  854.         }
  855.     }
  856.  
  857. success:
  858.     if (curr != y_current)        /* append the new block to the old block */
  859.     {
  860.         new_ptr = (char_u **)lalloc((long_u)(sizeof(char_u *) * (curr->y_size + y_current->y_size)), TRUE);
  861.         if (new_ptr == NULL)
  862.                 goto fail;
  863.         for (j = 0; j < curr->y_size; ++j)
  864.                 new_ptr[j] = curr->y_array[j];
  865.         free(curr->y_array);
  866.         curr->y_array = new_ptr;
  867.  
  868.         if (yanktype == MLINE)     /* MLINE overrides MCHAR and MBLOCK */
  869.                 curr->y_type = MLINE;
  870.         if (curr->y_type == MCHAR)        /* concatenate the last line of the old
  871.                                         block with the first line of the new block */
  872.         {
  873.                 pnew = lalloc((long_u)(STRLEN(curr->y_array[curr->y_size - 1]) + STRLEN(y_current->y_array[0]) + 1), TRUE);
  874.                 if (pnew == NULL)
  875.                 {
  876.                         i = y_current->y_size - 1;
  877.                         goto fail;
  878.                 }
  879.                 STRCPY(pnew, curr->y_array[--j]);
  880.                 STRCAT(pnew, y_current->y_array[0]);
  881.                 free(curr->y_array[j]);
  882.                 free(y_current->y_array[0]);
  883.                 curr->y_array[j++] = pnew;
  884.                 i = 1;
  885.         }
  886.         else
  887.                 i = 0;
  888.         while (i < y_current->y_size)
  889.                 curr->y_array[j++] = y_current->y_array[i++];
  890.         curr->y_size = j;
  891.         free(y_current->y_array);
  892.         y_current = curr;
  893.     }
  894.     if (operator == YANK)        /* don't do this when deleting */
  895.     {
  896.         if (yanktype == MCHAR && !Visual_block)
  897.             --yanklines;
  898.         if (yanklines > p_report)
  899.         {
  900.             cursupdate();        /* redisplay now, so message is not deleted */
  901.             smsg((char_u *)"%ld line%s yanked", yanklines, plural(yanklines));
  902.         }
  903.     }
  904.  
  905.     return OK;
  906. }
  907.  
  908. /*
  909.  * put contents of register into the text
  910.  */
  911.     void
  912. doput(dir, count, fix_indent)
  913.     int        dir;                /* BACKWARD for 'P', FORWARD for 'p' */
  914.     long    count;
  915.     int        fix_indent;            /* make indent look nice */
  916. {
  917.     char_u        *ptr;
  918.     char_u        *new, *old;
  919.     int         yanklen;
  920.     int            oldlen;
  921.     int            totlen = 0;        /* init for gcc */
  922.     linenr_t    lnum;
  923.     int            col;
  924.     long         i;        /* index in y_array[] */
  925.     int         y_type;
  926.     long         y_size;
  927.     char_u        **y_array;
  928.     long         nlines = 0;
  929.     int            vcol;
  930.     int            delchar;
  931.     int            incr = 0;
  932.     long        j;
  933.     FPOS        new_cursor;
  934.     int            commandchar;
  935.     char_u        temp[2];
  936.     int            indent;
  937.     int            orig_indent = 0;            /* init for gcc */
  938.     int            indent_diff = 0;            /* init for gcc */
  939.     int            first_indent = TRUE;
  940.  
  941.     if (fix_indent)
  942.         orig_indent = get_indent();
  943.  
  944.     curbuf->b_startop = curwin->w_cursor;            /* default for "'[" command */
  945.     if (dir == FORWARD)
  946.         curbuf->b_startop.col++;
  947.     curbuf->b_endop = curwin->w_cursor;                /* default for "']" command */
  948.     commandchar = (dir == FORWARD ? (count == -1 ? 'o' : 'a') : (count == -1 ? 'O' : 'i'));
  949.     if (yankbuffer == '.')        /* use inserted text */
  950.     {
  951.         stuff_inserted(commandchar, count, FALSE);
  952.         return;
  953.     }
  954.     else if (yankbuffer == '%')    /* use file name */
  955.     {
  956.         if (check_fname() == OK)
  957.         {
  958.             stuffcharReadbuff(commandchar);
  959.             stuffReadbuff(curbuf->b_xfilename);
  960.             stuffcharReadbuff(ESC);
  961.         }
  962.         return;
  963.     }
  964.     else if (yankbuffer == ':')    /* use last command line */
  965.     {
  966.         if (last_cmdline == NULL)
  967.             EMSG(e_nolastcmd);
  968.         else
  969.         {
  970.             stuffcharReadbuff(commandchar);
  971.             stuffReadbuff(last_cmdline);
  972.             stuffcharReadbuff(ESC);
  973.         }
  974.         return;
  975.     }
  976.  
  977.     get_yank_buffer(FALSE);
  978.  
  979.     y_type = y_current->y_type;
  980.     y_size = y_current->y_size;
  981.     y_array = y_current->y_array;
  982.  
  983.     if (count == -1)        /* :put command */
  984.     {
  985.         y_type = MLINE;
  986.         count = 1;
  987.     }
  988.  
  989.     if (y_size == 0 || y_array == NULL)
  990.     {
  991.         temp[0] = yankbuffer;
  992.         temp[1] = NUL;
  993.         EMSG2("Nothing in register %s", temp);
  994.         return;
  995.     }
  996.  
  997.     if (y_type == MBLOCK)
  998.     {
  999.         lnum = curwin->w_cursor.lnum + y_size + 1;
  1000.         if (lnum > curbuf->b_ml.ml_line_count)
  1001.             lnum = curbuf->b_ml.ml_line_count + 1;
  1002.         if (!u_save(curwin->w_cursor.lnum - 1, lnum))
  1003.             return;
  1004.     }
  1005.     else if (!u_save_cursor())
  1006.         return;
  1007.  
  1008.     yanklen = STRLEN(y_array[0]);
  1009.     CHANGED;
  1010.  
  1011.     lnum = curwin->w_cursor.lnum;
  1012.     col = curwin->w_cursor.col;
  1013.  
  1014. /*
  1015.  * block mode
  1016.  */
  1017.     if (y_type == MBLOCK)
  1018.     {
  1019.         if (dir == FORWARD && gchar_cursor() != NUL)
  1020.         {
  1021.             col = getvcol(curwin, &curwin->w_cursor, 3) + 1;
  1022.             ++curwin->w_cursor.col;
  1023.         }
  1024.         else
  1025.             col = getvcol(curwin, &curwin->w_cursor, 2);
  1026.         for (i = 0; i < y_size; ++i)
  1027.         {
  1028.             startspaces = 0;
  1029.             endspaces = 0;
  1030.             textcol = 0;
  1031.             vcol = 0;
  1032.             delchar = 0;
  1033.  
  1034.         /* add a new line */
  1035.             if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  1036.             {
  1037.                 ml_append(curbuf->b_ml.ml_line_count, (char_u *)"", (colnr_t)1, FALSE);
  1038.                 ++nlines;
  1039.             }
  1040.             old = ml_get(curwin->w_cursor.lnum);
  1041.             oldlen = STRLEN(old);
  1042.             for (ptr = old; vcol < col && *ptr; ++ptr)
  1043.             {
  1044.                 /* Count a tab for what it's worth (if list mode not on) */
  1045.                 incr = chartabsize(*ptr, (long)vcol);
  1046.                 vcol += incr;
  1047.                 ++textcol;
  1048.             }
  1049.             if (vcol < col)    /* line too short, padd with spaces */
  1050.             {
  1051.                 startspaces = col - vcol;
  1052.             }
  1053.             else if (vcol > col)
  1054.             {
  1055.                 endspaces = vcol - col;
  1056.                 startspaces = incr - endspaces;
  1057.                 --textcol;
  1058.                 delchar = 1;
  1059.             }
  1060.             yanklen = STRLEN(y_array[i]);
  1061.             totlen = count * yanklen + startspaces + endspaces;
  1062.             new = alloc((unsigned)totlen + oldlen + 1);
  1063.             if (new == NULL)
  1064.                 break;
  1065.         /* copy part up to cursor to new line */
  1066.             ptr = new;
  1067.             memmove((char *)ptr, (char *)old, (size_t)textcol);
  1068.             ptr += textcol;
  1069.         /* may insert some spaces before the new text */
  1070.             copy_spaces(ptr, (size_t)startspaces);
  1071.             ptr += startspaces;
  1072.         /* insert the new text */
  1073.             for (j = 0; j < count; ++j)
  1074.             {
  1075.                     STRNCPY(ptr, y_array[i], (size_t)yanklen);
  1076.                     ptr += yanklen;
  1077.             }
  1078.         /* may insert some spaces after the new text */
  1079.             copy_spaces(ptr, (size_t)endspaces);
  1080.             ptr += endspaces;
  1081.         /* move the text after the cursor to the end of the line. */
  1082.             memmove((char *)ptr, (char *)old + textcol + delchar,
  1083.                             (size_t)(oldlen - textcol - delchar + 1));
  1084.             ml_replace(curwin->w_cursor.lnum, new, FALSE);
  1085.  
  1086.             ++curwin->w_cursor.lnum;
  1087.             if (i == 0)
  1088.                 curwin->w_cursor.col += startspaces;
  1089.         }
  1090.         curbuf->b_endop.lnum = curwin->w_cursor.lnum - 1;        /* for "']" command */
  1091.         curbuf->b_endop.col = textcol + totlen - 1;
  1092.         curwin->w_cursor.lnum = lnum;
  1093.         cursupdate();
  1094.         updateScreen(VALID_TO_CURSCHAR);
  1095.     }
  1096.     else        /* not block mode */
  1097.     {
  1098.         if (y_type == MCHAR)
  1099.         {
  1100.     /* if type is MCHAR, FORWARD is the same as BACKWARD on the next character */
  1101.             if (dir == FORWARD && gchar_cursor() != NUL)
  1102.             {
  1103.                 ++col;
  1104.                 if (yanklen)
  1105.                 {
  1106.                     ++curwin->w_cursor.col;
  1107.                     ++curbuf->b_endop.col;
  1108.                 }
  1109.             }
  1110.             new_cursor = curwin->w_cursor;
  1111.         }
  1112.         else if (dir == BACKWARD)
  1113.     /* if type is MLINE, BACKWARD is the same as FORWARD on the previous line */
  1114.             --lnum;
  1115.  
  1116. /*
  1117.  * simple case: insert into current line
  1118.  */
  1119.         if (y_type == MCHAR && y_size == 1)
  1120.         {
  1121.             totlen = count * yanklen;
  1122.             if (totlen)
  1123.             {
  1124.                 old = ml_get(lnum);
  1125.                 new = alloc((unsigned)(STRLEN(old) + totlen + 1));
  1126.                 if (new == NULL)
  1127.                     return;                 /* alloc() will give error message */
  1128.                 memmove((char *)new, (char *)old, (size_t)col);
  1129.                 ptr = new + col;
  1130.                 for (i = 0; i < count; ++i)
  1131.                 {
  1132.                     memmove((char *)ptr, (char *)y_array[0], (size_t)yanklen);
  1133.                     ptr += yanklen;
  1134.                 }
  1135.                 memmove((char *)ptr, (char *)old + col, STRLEN(old + col) + 1);
  1136.                 ml_replace(lnum, new, FALSE);
  1137.                 curwin->w_cursor.col += (colnr_t)(totlen - 1);    /* put cursor on last putted char */
  1138.             }
  1139.             curbuf->b_endop = curwin->w_cursor;
  1140.             updateline();
  1141.         }
  1142.         else
  1143.         {
  1144.             if (y_type == MCHAR)
  1145.                 --y_size;
  1146.             while (--count >= 0)
  1147.             {
  1148.                 i = 0;
  1149.                 if (y_type == MCHAR)
  1150.                 {
  1151.                     /*
  1152.                      * Split the current line in two at the insert position.
  1153.                      * First insert y_array[size - 1] in front of second line.
  1154.                      * Then append y_array[0] to first line.
  1155.                      */
  1156.                     ptr = ml_get(lnum) + col;
  1157.                     totlen = STRLEN(y_array[y_size]);
  1158.                     new = alloc((unsigned)(STRLEN(ptr) + totlen + 1));
  1159.                     if (new == NULL)
  1160.                         goto error;
  1161.                     STRCPY(new, y_array[y_size]);
  1162.                     STRCAT(new, ptr);
  1163.                     ml_append(lnum, new, (colnr_t)0, FALSE);    /* insert second line */
  1164.                     free(new);
  1165.                     ++nlines;
  1166.  
  1167.                     old = ml_get(lnum);
  1168.                     new = alloc((unsigned)(col + yanklen + 1));
  1169.                     if (new == NULL)
  1170.                         goto error;
  1171.                                             /* copy first part of line */
  1172.                     memmove((char *)new, (char *)old, (size_t)col);
  1173.                                             /* append to first line */
  1174.                     memmove((char *)new + col, (char *)y_array[0],
  1175.                                             (size_t)(yanklen + 1));
  1176.                     ml_replace(lnum, new, FALSE);
  1177.  
  1178.                     curwin->w_cursor.lnum = lnum;
  1179.                     i = 1;
  1180.                 }
  1181.  
  1182.                 while (i < y_size)
  1183.                 {
  1184.                     if (ml_append(lnum++, y_array[i++], (colnr_t)0, FALSE) == FAIL)
  1185.                         goto error;
  1186.                     if (fix_indent)
  1187.                     {
  1188.                         curwin->w_cursor.lnum = lnum;
  1189.                         if (curbuf->b_p_si && *ml_get(lnum) == '#')
  1190.                             indent = 0;        /* Leave # lines at start */
  1191.                         else if (first_indent)
  1192.                         {
  1193.                             indent_diff = orig_indent - get_indent();
  1194.                             indent = orig_indent;
  1195.                             first_indent = FALSE;
  1196.                         }
  1197.                         else if ((indent = get_indent() + indent_diff) < 0)
  1198.                             indent = 0;
  1199.                         set_indent(indent, TRUE);
  1200.                     }
  1201.                     ++nlines;
  1202.                 }
  1203.                 if (y_type == MCHAR)
  1204.                     ++lnum;     /* lnum is now number of line below inserted lines */
  1205.             }
  1206.  
  1207.             curbuf->b_endop.lnum = lnum;        /* for "']" command */
  1208.             if (y_type == MLINE)
  1209.             {
  1210.                 curwin->w_cursor.col = 0;
  1211.                 curbuf->b_endop.col = 0;
  1212.                 if (dir == FORWARD)
  1213.                 {
  1214.                     updateScreen(NOT_VALID);        /* recompute curwin->w_botline */
  1215.                     ++curwin->w_cursor.lnum;
  1216.                 }
  1217.                     /* put cursor on first non-blank in last inserted line */
  1218.                 beginline(TRUE);
  1219.             }
  1220.             else        /* put cursor on first inserted character */
  1221.             {
  1222.                 if (col > 1)
  1223.                     curbuf->b_endop.col = col - 1;
  1224.                 else
  1225.                     curbuf->b_endop.col = 0;
  1226.                 curwin->w_cursor = new_cursor;
  1227.             }
  1228.  
  1229. error:
  1230.             if (y_type == MLINE)        /* for '[ */
  1231.             {
  1232.                 curbuf->b_startop.col = 0;
  1233.                 if (dir == FORWARD)
  1234.                     curbuf->b_startop.lnum++;
  1235.             }
  1236.             mark_adjust(curbuf->b_startop.lnum + (y_type == MCHAR), MAXLNUM, nlines);
  1237.             updateScreen(CURSUPD);
  1238.         }
  1239.     }
  1240.  
  1241.     msgmore(nlines);
  1242.     curwin->w_set_curswant = TRUE;
  1243. }
  1244.  
  1245. /*
  1246.  * display the contents of the yank buffers
  1247.  */
  1248.     void
  1249. dodis()
  1250. {
  1251.     register int            i, n;
  1252.     register long            j;
  1253.     register char_u            *p;
  1254.     register struct yankbuf *yb;
  1255.  
  1256.     gotocmdline(TRUE, NUL);
  1257.  
  1258.     msg_outstr((char_u *)"--- Registers ---");
  1259.     for (i = -1; i < 36; ++i)
  1260.     {
  1261.         if (i == -1)
  1262.         {
  1263.             if (y_previous != NULL)
  1264.                 yb = y_previous;
  1265.             else
  1266.                 yb = &(y_buf[0]);
  1267.         }
  1268.         else
  1269.             yb = &(y_buf[i]);
  1270.         if (yb->y_array != NULL)
  1271.         {
  1272.             msg_outchar('\n');
  1273.             if (i == -1)
  1274.                 msg_outstr((char_u *)"\"\"");
  1275.             else
  1276.             {
  1277.                 msg_outchar('"');
  1278.                 if (i < 10)
  1279.                     msg_outchar(i + '0');
  1280.                 else
  1281.                     msg_outchar(i + 'a' - 10);
  1282.             }
  1283.             msg_outstr((char_u *)"   ");
  1284.  
  1285.             n = (int)Columns - 6;
  1286.             for (j = 0; j < yb->y_size && n > 1; ++j)
  1287.             {
  1288.                 if (j)
  1289.                 {
  1290.                     msg_outstr((char_u *)"^J");
  1291.                     n -= 2;
  1292.                 }
  1293.                 for (p = yb->y_array[j]; *p && (n -= charsize(*p)) >= 0; ++p)
  1294.                     msg_outtrans(p, 1);
  1295.             }
  1296.             flushbuf();                /* show one line at a time */
  1297.         }
  1298.     }
  1299.  
  1300.     /*
  1301.      * display last inserted text
  1302.      */
  1303.     if ((p = get_last_insert()) != NULL)
  1304.     {
  1305.         msg_outstr((char_u *)"\n\".   ");
  1306.         dis_msg(p, TRUE);
  1307.     }
  1308.  
  1309.     /*
  1310.      * display last command line
  1311.      */
  1312.     if (last_cmdline != NULL)
  1313.     {
  1314.         msg_outstr((char_u *)"\n\":   ");
  1315.         dis_msg(last_cmdline, FALSE);
  1316.     }
  1317.  
  1318.     /*
  1319.      * display current file name
  1320.      */
  1321.     if (curbuf->b_xfilename != NULL)
  1322.     {
  1323.         msg_outstr((char_u *)"\n\"%   ");
  1324.         dis_msg(curbuf->b_xfilename, FALSE);
  1325.     }
  1326.  
  1327.     msg_end();
  1328. }
  1329.  
  1330. /*
  1331.  * display a string for dodis()
  1332.  * truncate at end of screen line
  1333.  */
  1334.     void
  1335. dis_msg(p, skip_esc)
  1336.     char_u        *p;
  1337.     int            skip_esc;            /* if TRUE, ignore trailing ESC */
  1338. {
  1339.     int        n;
  1340.  
  1341.     n = (int)Columns - 6;
  1342.     while (*p && !(*p == ESC && skip_esc && *(p + 1) == NUL) &&
  1343.                         (n -= charsize(*p)) >= 0)
  1344.         msg_outtrans(p++, 1);
  1345. }
  1346.  
  1347. /*
  1348.  * join 'count' lines (minimal 2), including u_save()
  1349.  */
  1350.     void
  1351. dodojoin(count, insert_space, redraw)
  1352.     long    count;
  1353.     int        insert_space;
  1354.     int        redraw;
  1355. {
  1356.     if (!u_save((linenr_t)(curwin->w_cursor.lnum - 1), (linenr_t)(curwin->w_cursor.lnum + count)))
  1357.         return;
  1358.  
  1359.     while (--count > 0)
  1360.         if (dojoin(insert_space, redraw) == FAIL)
  1361.         {
  1362.                 beep();
  1363.                 break;
  1364.         }
  1365.  
  1366.     if (redraw)
  1367.         updateScreen(VALID_TO_CURSCHAR);
  1368. }
  1369.  
  1370. /*
  1371.  * join two lines at the cursor position
  1372.  *
  1373.  * return FAIL for failure, OK ohterwise
  1374.  */
  1375.     int
  1376. dojoin(insert_space, redraw)
  1377.     int            insert_space;
  1378.     int            redraw;
  1379. {
  1380.     char_u        *curr;
  1381.     char_u        *next;
  1382.     char_u        *new;
  1383.     int            endcurr1, endcurr2;
  1384.     int         currsize;        /* size of the current line */
  1385.     int         nextsize;        /* size of the next line */
  1386.     int            spaces;            /* number of spaces to insert */
  1387.     int            rows_to_del;    /* number of rows on screen to delete */
  1388.     linenr_t    t;
  1389.  
  1390.     if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)        /* on last line */
  1391.         return FAIL;
  1392.  
  1393.     rows_to_del = plines_m(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1);
  1394.  
  1395.     curr = ml_get(curwin->w_cursor.lnum);
  1396.     currsize = STRLEN(curr);
  1397.     endcurr1 = endcurr2 = NUL;
  1398.     if (currsize > 0)
  1399.     {
  1400.         endcurr1 = *(curr + currsize - 1);
  1401.         if (currsize > 1)
  1402.             endcurr2 = *(curr + currsize - 2);
  1403.     }
  1404.  
  1405.     next = ml_get((linenr_t)(curwin->w_cursor.lnum + 1));
  1406.     spaces = 0;
  1407.     if (insert_space)
  1408.     {
  1409.         skipspace(&next);
  1410.         spaces = 1;
  1411.         if (*next == ')' || currsize == 0)
  1412.             spaces = 0;
  1413.         else
  1414.         {
  1415.             if (endcurr1 == ' ' || endcurr1 == TAB)
  1416.             {
  1417.                 spaces = 0;
  1418.                 if (currsize > 1)
  1419.                     endcurr1 = endcurr2;
  1420.             }
  1421.             if (p_js && strchr(".!?", endcurr1) != NULL)
  1422.                 spaces = 2;
  1423.         }
  1424.     }
  1425.     nextsize = STRLEN(next);
  1426.  
  1427.     new = alloc((unsigned)(currsize + nextsize + spaces + 1));
  1428.     if (new == NULL)
  1429.         return FAIL;
  1430.  
  1431.     /*
  1432.      * Insert the next line first, because we already have that pointer.
  1433.      * Curr has to be obtained again, because getting next will have
  1434.      * invalidated it.
  1435.      */
  1436.     memmove((char *)new + currsize + spaces, (char *)next, (size_t)(nextsize + 1));
  1437.  
  1438.     curr = ml_get(curwin->w_cursor.lnum);
  1439.     memmove((char *)new, (char *)curr, (size_t)currsize);
  1440.  
  1441.     copy_spaces(new + currsize, (size_t)spaces);
  1442.  
  1443.     ml_replace(curwin->w_cursor.lnum, new, FALSE);
  1444.  
  1445.     /*
  1446.      * Delete the following line. To do this we move the cursor there
  1447.      * briefly, and then move it back. After dellines() the cursor may
  1448.      * have moved up (last line deleted), so the current lnum is kept in t.
  1449.      */
  1450.     t = curwin->w_cursor.lnum;
  1451.     ++curwin->w_cursor.lnum;
  1452.     dellines(1L, FALSE, FALSE);
  1453.     curwin->w_cursor.lnum = t;
  1454.  
  1455.     /*
  1456.      * the number of rows on the screen is reduced by the difference
  1457.      * in number of rows of the two old lines and the one new line
  1458.      */
  1459.     if (redraw)
  1460.     {
  1461.         rows_to_del -= plines(curwin->w_cursor.lnum);
  1462.         if (rows_to_del > 0)
  1463.             win_del_lines(curwin, curwin->w_row, rows_to_del, TRUE, TRUE);
  1464.     }
  1465.  
  1466.      /*
  1467.      * go to first character of the joined line
  1468.      */
  1469.     if (currsize == 0)
  1470.         curwin->w_cursor.col = 0;
  1471.     else
  1472.     {
  1473.         curwin->w_cursor.col = currsize - 1;
  1474.         (void)oneright();
  1475.     }
  1476.     CHANGED;
  1477.  
  1478.     return OK;
  1479. }
  1480.  
  1481. /*
  1482.  * implementation of the format operator 'Q'
  1483.  */
  1484.     void
  1485. doformat()
  1486. {
  1487.         /* prepare undo and join the lines */
  1488.     dodojoin((long)nlines, TRUE, FALSE);
  1489.  
  1490.         /* put cursor on last non-space */
  1491.     coladvance(MAXCOL);
  1492.     while (curwin->w_cursor.col && isspace(gchar_cursor()))
  1493.         dec_cursor();
  1494.     curs_columns(FALSE);            /* update curwin->w_virtcol */
  1495.  
  1496.         /* do the formatting */
  1497.     State = INSERT;        /* for Opencmd() */
  1498.     insertchar(NUL);
  1499.     State = NORMAL;
  1500.     updateScreen(NOT_VALID);
  1501. }
  1502.  
  1503.     void
  1504. startinsert(initstr, startln, count)
  1505.     int            initstr;
  1506.     int         startln;        /* if set, insert at start of line */
  1507.     long         count;
  1508. {
  1509.     Insstart = curwin->w_cursor;
  1510.     if (startln)
  1511.         Insstart.col = 0;
  1512.  
  1513.     if (initstr != NUL)
  1514.     {
  1515.             ResetRedobuff();
  1516.             AppendNumberToRedobuff(count);
  1517.             AppendCharToRedobuff(initstr);
  1518.     }
  1519.  
  1520.     if (initstr == 'R')
  1521.         State = REPLACE;
  1522.     else
  1523.         State = INSERT;
  1524.  
  1525.     if (p_smd)
  1526.         showmode();
  1527.  
  1528.     change_warning();        /* give a warning if readonly */
  1529.     edit(count);
  1530. }
  1531.  
  1532. /*
  1533.  * prepare a few things for block mode yank/delete/tilde
  1534.  *
  1535.  * for delete:
  1536.  * - textlen includes the first/last char to be (partly) deleted
  1537.  * - start/endspaces is the number of columns that are taken by the
  1538.  *     first/last deleted char minus the number of columns that have to be deleted.
  1539.  * for yank and tilde:
  1540.  * - textlen includes the first/last char to be wholly yanked
  1541.  * - start/endspaces is the number of columns of the first/last yanked char
  1542.  *   that are to be yanked.
  1543.  */
  1544.     static void
  1545. block_prep(lnum, delete)
  1546.     linenr_t    lnum;
  1547.     int            delete;
  1548. {
  1549.     int            vcol;
  1550.     int            incr = 0;
  1551.     char_u        *pend;
  1552.  
  1553.     startspaces = 0;
  1554.     endspaces = 0;
  1555.     textlen = 0;
  1556.     textcol = 0;
  1557.     vcol = 0;
  1558.     textstart = ml_get(lnum);
  1559.     while (vcol < startvcol && *textstart)
  1560.     {
  1561.         /* Count a tab for what it's worth (if list mode not on) */
  1562.         incr = chartabsize(*textstart, (long)vcol);
  1563.         vcol += incr;
  1564.         ++textstart;
  1565.         ++textcol;
  1566.     }
  1567.     if (vcol < startvcol)    /* line too short */
  1568.     {
  1569.         if (!delete)
  1570.             endspaces = endvcol - startvcol + 1;
  1571.     }
  1572.     else /* vcol >= startvcol */
  1573.     {
  1574.         startspaces = vcol - startvcol;
  1575.         if (delete && vcol > startvcol)
  1576.             startspaces = incr - startspaces;
  1577.         pend = textstart;
  1578.         if (vcol > endvcol)        /* it's all in one character */
  1579.         {
  1580.             startspaces = endvcol - startvcol + 1;
  1581.             if (delete)
  1582.                 startspaces = incr - startspaces;
  1583.         }
  1584.         else
  1585.         {
  1586.             while (vcol <= endvcol && *pend)
  1587.             {
  1588.                 /* Count a tab for what it's worth (if list mode not on) */
  1589.                 incr = chartabsize(*pend, (long)vcol);
  1590.                 vcol += incr;
  1591.                 ++pend;
  1592.             }
  1593.             if (vcol < endvcol && !delete)    /* line too short */
  1594.             {
  1595.                 endspaces = endvcol - vcol;
  1596.             }
  1597.             else if (vcol > endvcol)
  1598.             {
  1599.                 endspaces = vcol - endvcol - 1;
  1600.                 if (!delete && pend != textstart && endspaces)
  1601.                     --pend;
  1602.             }
  1603.         }
  1604.         if (delete && startspaces)
  1605.         {
  1606.             --textstart;
  1607.             --textcol;
  1608.         }
  1609.         textlen = (int)(pend - textstart);
  1610.     }
  1611. }
  1612.  
  1613. #define NUMBUFLEN 30
  1614.  
  1615. /*
  1616.  * add or subtract 'Prenum1' from a number in a line
  1617.  * 'command' is CTRL-A for add, CTRL-X for subtract
  1618.  *
  1619.  * return FAIL for failure, OK otherwise
  1620.  */
  1621.     int
  1622. doaddsub(command, Prenum1)
  1623.     int            command;
  1624.     linenr_t    Prenum1;
  1625. {
  1626.     register int     col;
  1627.     char_u            buf[NUMBUFLEN];
  1628.     int                hex;        /* 'x' or 'X': hexadecimal; '0': octal */
  1629.     static int        hexupper = FALSE;    /* 0xABC */
  1630.     long            n;
  1631.     char_u            *ptr;
  1632.     int                i;
  1633.     int                c;
  1634.  
  1635.     ptr = ml_get(curwin->w_cursor.lnum);
  1636.     col = curwin->w_cursor.col;
  1637.  
  1638.         /* first check if we are on a hexadecimal number */
  1639.     while (col > 0 && isxdigit(ptr[col]))
  1640.         --col;
  1641.     if (col > 0 && (ptr[col] == 'X' || ptr[col] == 'x') &&
  1642.                         ptr[col - 1] == '0' && isxdigit(ptr[col + 1]))
  1643.         --col;        /* found hexadecimal number */
  1644.     else
  1645.     {
  1646.         /* first search forward and then backward for start of number */
  1647.         col = curwin->w_cursor.col;
  1648.  
  1649.         while (ptr[col] != NUL && !isdigit(ptr[col]))
  1650.             ++col;
  1651.  
  1652.         while (col > 0 && isdigit(ptr[col - 1]))
  1653.             --col;
  1654.     }
  1655.  
  1656.     if (isdigit(ptr[col]) && u_save_cursor())
  1657.     {
  1658.         ptr = ml_get(curwin->w_cursor.lnum);    /* get it again, because of undo */
  1659.         curwin->w_set_curswant = TRUE;
  1660.  
  1661.         hex = 0;                                /* default is decimal */
  1662.         if (ptr[col] == '0')                    /* could be hex or octal */
  1663.         {
  1664.             hex = TO_UPPER(ptr[col + 1]);        /* assume hexadecimal */
  1665.             if (hex != 'X' || !isxdigit(ptr[col + 2]))
  1666.             {
  1667.                 if (isdigit(hex))
  1668.                     hex = '0';                    /* octal */
  1669.                 else
  1670.                     hex = 0;                    /* 0 by itself is decimal */
  1671.             }
  1672.         }
  1673.  
  1674.         if (!hex && col > 0 && ptr[col - 1] == '-')
  1675.             --col;
  1676.  
  1677.         ptr += col;
  1678.         /*
  1679.          * we copy the number into a buffer because some versions of sscanf
  1680.          * cannot handle characters with the upper bit set, making some special
  1681.          * characters handled like digits.
  1682.          */
  1683.         for (i = 0; *ptr && !(*ptr & 0x80) && i < NUMBUFLEN - 1; ++i)
  1684.             buf[i] = *ptr++;
  1685.         buf[i] = NUL;
  1686.  
  1687.         if (hex == '0')
  1688.             sscanf((char *)buf, "%lo", &n);
  1689.         else if (hex)
  1690.             sscanf((char *)buf + 2, "%lx", &n);    /* "%X" doesn't work! */
  1691.         else
  1692.             n = atol((char *)buf);
  1693.  
  1694.         if (command == Ctrl('A'))
  1695.             n += Prenum1;
  1696.         else
  1697.             n -= Prenum1;
  1698.  
  1699.         if (hex == 'X')                    /* skip the '0x' */
  1700.             col += 2;
  1701.         curwin->w_cursor.col = col;
  1702.         c = gchar_cursor();
  1703.         do                                /* delete the old number */
  1704.         {
  1705.             if (isalpha(c))
  1706.             {
  1707.                 if (isupper(c))
  1708.                     hexupper = TRUE;
  1709.                 else
  1710.                     hexupper = FALSE;
  1711.             }
  1712.             (void)delchar(FALSE);
  1713.             c = gchar_cursor();
  1714.         }
  1715.         while (hex ? (hex == '0' ? c >= '0' && c <= '7' : isxdigit(c)) : isdigit(c));
  1716.  
  1717.         if (hex == '0')
  1718.             sprintf((char *)buf, "0%lo", n);
  1719.         else if (hex && hexupper)
  1720.             sprintf((char *)buf, "%lX", n);
  1721.         else if (hex)
  1722.             sprintf((char *)buf, "%lx", n);
  1723.         else
  1724.             sprintf((char *)buf, "%ld", n);
  1725.         insstr(buf);                    /* insert the new number */
  1726.         --curwin->w_cursor.col;
  1727.         updateline();
  1728.         return OK;
  1729.     }
  1730.     else
  1731.     {
  1732.         beep();
  1733.         return FAIL;
  1734.     }
  1735. }
  1736.